#pragma once

#include <map>
#include "mswin/event.h"

namespace icache {

struct subscription {
	subscription() : frame_read(0), counter(0)
		{ }
	mswin::event * frame_read; ///< event sets whenever awaited frame is read from AVI
	int counter;
};

class subscriptions : private mswin::shared_resource
{
public:
	~subscriptions() {
		map_type::iterator i;
		for (i = map.begin(); i != map.end(); ++i)
			delete i->second.frame_read;
	}
	///caller received event, on which it can wait until frame is read
	mswin::event const & subscribe(int frame) {
		auto_lock lock(*this);
		subscription & s = map[frame];
		if (s.counter == 0)
			s.frame_read = new mswin::event(false);
		++s.counter;
		return * s.frame_read;
	}
	void unsubscribe(int frame) {
		map_type::iterator i = map.find(frame);
		assert(i != map.end() && i->first == frame && i->second.counter != 0);
		if (--i->second.counter == 0) {
			delete i->second.frame_read;
			map.erase(i);
		}
	}
	bool on_frame(int frame) {
		auto_lock lock(*this);
		map_type::iterator i = map.find(frame);
		return i != map.end() && i->first == frame;
	}
	///if there are subscribers for the given frame, then appropriate event is set
	void event(int frame) {
		auto_lock lock(*this);
		map_type::iterator i = map.find(frame);
		if (i != map.end() && i->first == frame)
			//i->second.frame_read->pulse(); //doesn't work sometimes if loading is fast (Platform SDK: PulseEvent is unreliable and should not be used by new applications)
			i->second.frame_read->set();
	}
private:
	typedef std::map<int, subscription> map_type;
	map_type map;
};

class subscriber
{
public:
	subscriber(subscriptions & ss, int f) : s(ss), frame(f), read_event(ss.subscribe(f))
		{ }
	~subscriber()
		{ s.unsubscribe(frame); }
	void wait()
		{ read_event.wait(); }
private:
	subscriptions & s;
	int frame;
	mswin::event const & read_event;
};

} //namespace icache
